package authlike

import (
	"context"
	"errors"
	"fmt"
	"strconv"
	"strings"
	"time"

	"blogcenter/internal/middleware"
	"blogcenter/internal/model"
	"blogcenter/internal/svc"
	"blogcenter/internal/types"

	"github.com/zeromicro/go-zero/core/logx"
	"github.com/zeromicro/go-zero/core/stores/redis"
	"gorm.io/gorm"
)

type LikeCommentLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

const (
	commentLikeKeyPrefix     = "comment:like:"
	commentLikeCountKey      = "comment:likecnt:"
	commentlikeLockKeyPrefix = "comment:like:lock:" // 分布式锁键前缀
)

func NewLikeCommentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LikeCommentLogic {
	return &LikeCommentLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *LikeCommentLogic) LikeComment(req *types.IdRequest) (resp *types.BaseResponse, err error) {
	// 1. 参数校验
	if req.Id <= 0 {
		return nil, errors.New("无效的评论ID")
	}

	// 2. 从上下文中获取用户ID
	userId, ok := l.ctx.Value(middleware.UserIdKey).(int)
	if !ok || userId == 0 {
		return nil, errors.New("未授权访问")
	}

	// 3. 获取分布式锁
	lockKey := fmt.Sprintf("%s%d:%d", commentlikeLockKeyPrefix, req.Id, userId)
	lockSuccess, err := l.svcCtx.Redis.SetnxEx(lockKey, "1", int(likeLockExpiration.Seconds()))
	if err != nil {
		logx.Errorf("获取分布式锁失败, key: %s, error: %v", lockKey, err)
		return nil, errors.New("系统繁忙，请稍后再试")
	}
	if !lockSuccess {
		return nil, errors.New("操作过于频繁，请稍后再试")
	}
	defer func() {
		if _, err := l.svcCtx.Redis.Del(lockKey); err != nil {
			logx.Errorf("释放分布式锁失败, key: %s, error: %v", lockKey, err)
		}
	}()

	// 4. 检查用户点赞状态
	userLikeKey := fmt.Sprintf("%s%d:%d", commentLikeKeyPrefix, req.Id, userId)
	hasLiked, err := l.svcCtx.Redis.Get(userLikeKey)
	if err != nil && err != redis.Nil {
		logx.Errorf("检查用户点赞状态失败, key: %s, error: %v", userLikeKey, err)
		return nil, errors.New("系统错误，请稍后再试")
	}

	// 5. 处理点赞/取消点赞
	var action int
	var countChange int
	if hasLiked == "1" {
		// 取消点赞
		action = 0
		countChange = -1
		if _, err := l.svcCtx.Redis.Del(userLikeKey); err != nil {
			logx.Errorf("取消点赞失败, key: %s, error: %v", userLikeKey, err)
			return nil, errors.New("取消点赞失败")
		}
	} else {
		// 点赞
		action = 1
		countChange = 1
		if err := l.svcCtx.Redis.Setex(userLikeKey, "1", int((24 * time.Hour).Seconds())); err != nil {
			logx.Errorf("设置点赞状态失败, key: %s, error: %v", userLikeKey, err)
			return nil, errors.New("点赞失败")
		}
	}

	// 6. 更新点赞计数
	likeCountKey := fmt.Sprintf("%s%d", commentLikeCountKey, req.Id)
	if _, err := l.svcCtx.Redis.Incrby(likeCountKey, int64(countChange)); err != nil {
		logx.Errorf("更新点赞计数失败, commentId: %d, error: %v", req.Id, err)
		// 这里不返回错误，因为主要操作已经成功
	}

	// 7. 异步更新数据库和缓存
	go func() {
		// 先更新数据库
		l.asyncUpdateLikeStatus(req.Id, userId, action, countChange)

		// 然后更新缓存
		l.updateCacheLikeCount(req.Id, countChange)
	}()

	return &types.BaseResponse{
		Code:    200,
		Message: "操作成功",
	}, nil
}

// 异步更新点赞状态到数据库
func (l *LikeCommentLogic) asyncUpdateLikeStatus(commentId int, userId int, action int, countChange int) {
	// 1. 检查评论是否存在
	_, err := l.svcCtx.CommentModel.FindIndex(context.Background(), uint(commentId))
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			logx.Errorf("评论不存在, commentId: %d", commentId)
			return
		}
		logx.Errorf("查询评论失败, commentId: %d, error: %v", commentId, err)
		return
	}

	// 2. 更新点赞记录
	likeRecord := &model.CommentLike{
		CommentIndexId: commentId,
		UserId:         userId,
		LikeAction:     action,
	}

	// 使用事务确保数据一致性
	err = l.svcCtx.DB.Transaction(func(tx *gorm.DB) error {
		// 查找现有记录
		var existing model.CommentLike
		if err := tx.Where("comment_index_id = ? AND user_id = ?", commentId, userId).First(&existing).Error; err != nil {
			if !errors.Is(err, gorm.ErrRecordNotFound) {
				return err
			}
			// 记录不存在，创建新记录
			return tx.Create(likeRecord).Error
		}

		// 记录存在，更新状态
		if err := tx.Model(&existing).Update("like_action", action).Error; err != nil {
			return err
		}

		// 更新评论点赞总数
		return tx.Model(&model.CommentIndex{}).
			Where("id = ?", commentId).
			UpdateColumn("like_count", gorm.Expr("like_count + ?", countChange)).
			Error
	})

	if err != nil {
		logx.Errorf("更新点赞状态到数据库失败, commentId: %d, userId: %d, action: %d, error: %v",
			commentId, userId, action, err)
	}
}

// 启动点赞同步服务
func StartCommentLikeSyncService(svcCtx *svc.ServiceContext) {
	ticker := time.NewTicker(likeSyncInterval)
	defer ticker.Stop()

	for range ticker.C {
		syncCommentLikeCountsToDB(svcCtx)
	}
}

// 同步点赞计数到数据库
func syncCommentLikeCountsToDB(svcCtx *svc.ServiceContext) {
	// 获取所有点赞计数键
	keys, err := svcCtx.Redis.Keys(fmt.Sprintf("%s*", commentLikeCountKey))
	if err != nil {
		logx.Errorf("获取点赞计数键失败: %v", err)
		return
	}

	for _, key := range keys {
		// 从键名中提取评论ID
		commentIdStr := strings.TrimPrefix(key, commentLikeCountKey)
		commentId, err := strconv.Atoi(commentIdStr)
		if err != nil {
			logx.Errorf("解析评论ID失败, key: %s, error: %v", key, err)
			continue
		}

		// 获取点赞计数
		countStr, err := svcCtx.Redis.Get(key)
		if err != nil {
			logx.Errorf("获取点赞计数失败, commentId: %d, error: %v", commentId, err)
			continue
		}

		count, err := strconv.Atoi(countStr)
		if err != nil {
			logx.Errorf("解析点赞计数失败, commentId: %d, count: %s, error: %v", commentId, countStr, err)
			continue
		}

		// 如果计数为0，跳过
		if count == 0 {
			continue
		}

		// 更新数据库
		err = svcCtx.CommentModel.IncrementLikeCount(context.Background(), uint(commentId), count)
		if err != nil {
			logx.Errorf("更新数据库点赞数失败, commentId: %d, count: %d, error: %v", commentId, count, err)
			continue
		}

		// 重置计数为0
		err = svcCtx.Redis.Set(key, "0")
		if err != nil {
			logx.Errorf("重置点赞计数失败, commentId: %d, error: %v", commentId, err)
		}
	}
}

// 更新缓存中的点赞数
func (l *LikeCommentLogic) updateCacheLikeCount(commentId int, countChange int) {
	// 检查评论是否存在
	commentindex, err := l.svcCtx.CommentModel.FindIndex(context.Background(), uint(commentId))
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			logx.Errorf("评论不存在: %d", commentId)
		}
		logx.Errorf("查询评论失败: %v", err)
	}
	// 1. 构造点赞排序集合的key（与存储时一致）
	likeKey := fmt.Sprintf("comment_index:like:%d:%d:0", commentindex.ObjectType, commentindex.ObjectId)

	// 2. 获取当前点赞数
	currentScore, err := l.svcCtx.Redis.Zscore(likeKey, strconv.Itoa(commentId))
	if err != nil && err != redis.Nil {
		logx.Errorf("获取点赞数失败: %v", err)
	}

	// 3. 计算新的点赞数（处理初始值为0的情况）
	var newScore int64
	if err == redis.Nil {
		// 键不存在时，初始化为0 + countChange
		newScore = int64(countChange)
	} else {
		// 已有值则增减
		newScore = currentScore + int64(countChange)
	}

	// 4. 禁止点赞数变为负数
	if newScore < 0 {
		newScore = 0
	}

	// 5. 更新Redis中的点赞数
	_, err = l.svcCtx.Redis.Zadd(likeKey, newScore, strconv.Itoa(commentId))
	if err != nil {
		logx.Errorf("更新点赞数失败: %v", err)
	}
}
